/**
 * call及其实现
 */

 // call() 方法在使用一个指定的 this 值和若干个指定的参数值的前提下调用某个函数或方法。
 //call()、apply()、bind() 都是用来重定义 this 这个对象的
 var foo = {
   value: 1
 }
 function bar(){
   console.log(this.value);
 }
 bar.call(foo);

 // 模拟call的实现
 Function.prototype.call2 = function(context){
   var context = context || window // this参数可以传null, window在浏览器环境中才有
   context.fn = this;

   var args = [];
   for(var i = 1;i<arguments.length;i++){
     args.push('arguments['+ i +']');
   }
   // console.log(this,'this'); this此时为bar
   var result = eval('context.fn('+ args + ')');
   delete context.fn;
   return result;
 }

var value = 2;
var foo = {
  value: 1
}
function bar(name,age){
  // console.log(name,age);
  console.log(this.value);
}
bar.call2(foo,'cc',22);
// bar.call2(null);

//apply的模拟实现
Function.prototype.apply2 = function(context,arr){
  var context = context || window;
  context.fn = this;
  var result;
  if(!arr){
    result = context.fn()
  }else {
      var args = [];
      for(var i = 0;i<arr.length;i++){
        args.push('arr['+ i +']');
      }
      result = eval('context.fn(' + args + ')')
  }
  delete context.fn;
  return result;
}

/**
 * bind:bind() 方法会创建一个新函数。当这个新函数被调用时，bind() 的第一个参数将作为它运行时的 this
 * ，之后的一序列参数将会在传递的实参前传入作为它的参数。(来自于 MDN )
 * 详细分析过程见：https://github.com/mqyqingfeng/Blog/issues/12
 */
 Function.prototype.bind2 = function (context) {
    if(typeof this !== 'function'){
        throw new Error('function.prototype.bind--what is trying to be bound is not callable')
    }
    var self = this;
    var args = Array.prototype.slice.call(arguments, 1);


    var fBound = function () {
        var bindArgs = Array.prototype.slice.call(arguments);
        //bind 返回的函数作为构造函数的时候，bind 时指定的 this 值会失效，但传入的参数依然生效
        return self.apply(this instanceof fBound? this : context, args.concat(bindArgs));
    }

    //修改返回函数的 prototype 为绑定函数的 prototype，实例就可以继承绑定函数的原型中的值
    fBound.prototype = Object.create(this.prototype);
    return fBound;
}
  var foo = {
    value: 1
  };

  function bar(name,age,num,address) {
    console.log(name,age,num,address)
    console.log(this.value);
  }

  // 返回了一个函数
  var bindFoo = bar.bind2(foo,'cc',22);
  bindFoo(111,'hz'); // 1
